#ifndef SCENE_H
#define SCENE_H

//#include "entt.hpp"
// #include "Core/timestep.h"
#include "Core/UUID.h"
#include "Renderer/perspectiveCamera.h"

#include "Renderer/rendererAPI.h"
#include "Scene/AnimationProperty.h"
#include "Scene/Components.h"
#include "Scene/Resource_manager.h"
#include "Renderer/font.h"
#include "Core/tinyxml2.h"

#include "Renderer/Renderer2D.h"
#include "Renderer/Renderer3D.h"

namespace CC
{ 

	class Entity;
    class OrthographicCamera;
    class PerspectiveCamera;

    typedef struct  
    {
        std::string name;
        std::string path;
        std::string icon;
        Entity* hasExistObj;
    }DesktopIconInfo;

    typedef struct
    {
        std::string name;
        std::string type;
        std::string icon;
        int scale;
        Entity* hasExistObj;
    }PhotoIconInfo;

    typedef struct
    {
        std::string name;
        std::string path;
        int numberLenth;
        std::string format;
        int start;
        int end;
        float fps;
        int scale;
        float timeStep;//1.0/fps=s
        float currentTime;//s
        int currentFrameIndex;
        Entity* hasExistObj;
    }VideoInfo;

	class Scene
	{
	public:
		Scene();
		~Scene();

		Entity *CreateEntity(const std::string &name = std::string(), Entity *parent = nullptr);
		Entity *CreateEntityWithUUID(UUID uuid, const std::string &name = std::string(), Entity *parent = nullptr);
		Entity *CreateRoot(const std::string &name = std::string());

		void DestroyEntity(Entity *entity);
        Entity* FindEntityByName(const std::string name);
        Entity* FindEntityByRigidBoy(const btRigidBody *body);
        Entity* FindEntityByCamera2d();
        Entity* FindEntityByCamera3d();

		void OnRuntimeStart();
		void OnRuntimeStop();

		void OnSimulationStart();
		void OnSimulationStop();

		void OnPhysics3DStart();
		void OnPhysics3DStop();

		void OnUpdateRuntime(Timestep ts, std::string renderscene = "");
		void OnUpdateSimulation(Timestep ts, Camera *camera);
		void OnUpdateAnimationProperty(Timestep ts);
		void OnUpdateAnimationPropertyGroup(Timestep ts);
		void OnUpdateScript(Timestep ts);
		void OnUpdateTipInfo(Timestep ts);

		void OnViewportResize(uint32_t x, uint32_t y, uint32_t width, uint32_t height);

		Entity *DuplicateEntity(Entity *entity);
		
		Entity *GetEntityByUUID(UUID uuid);

		Entity *GetPrimaryCameraEntity();

		bool IsRunning() const { return m_IsRunning; }
		bool IsPaused() const { return m_IsPaused; }

		void SetPaused(bool paused) { m_IsPaused = paused; }

		void Step(int frames = 1);

		void RenderScene2D(Camera *camera, glm::mat4 cameraTransform, Timestep ts);
		void RenderScene3D(Camera *camera, glm::mat4 cameraTransform, Timestep ts);
		void RenderScene3DDebugPhysics(Camera *camera, glm::mat4 cameraTransform, Timestep ts);

		void RenderEnityty2D(Entity *e = nullptr, glm::mat4 parentTransform = glm::mat4(1.0), Timestep ts = Timestep());
        void RenderEnityty3D(Entity *e = nullptr, glm::mat4 parentTransform = glm::mat4(1.0), glm::vec3 parentScale = glm::vec3(1.0, 1.0, 1.0), Timestep ts = Timestep(), std::string renderPass="");
		void RenderEnityty3DDebugPhysics(Entity *e = nullptr, glm::mat4 parentTransform = glm::mat4(1.0), glm::vec3 parentScale = glm::vec3(1.0, 1.0, 1.0), Timestep ts = Timestep());

		void SetDisplayMode(DisplayModesEnum mode);
        DisplayModesEnum GetDisplayMode() {
            return m_DisplayMode;
        }

		void OnComponentAdded(Entity *entity, Component* component);

		void OnComponentRemoved(Entity *entity, Component* component);

		btDiscreteDynamicsWorld *GetDynamicsWorld() { return m_dynamicsWorld; }

		btVector3 GetRayTo(int x, int y);
		bool PickBody(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, Entity** entity);
		bool PickDynamicCloseBody(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, Entity** entity);
        bool PickStaticBody(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, Entity** entity, float* pickpos=NULL);
        std::vector<btRigidBody*> PickAllStaticBody(const btVector3 &rayFromWorld, const btVector3 &rayToWorld);
        bool PickAllStaticBodyFilter(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, Entity** entity, float* pickpos, std::vector<Entity*> filterObjectes);
        bool PickAllBodyFilter(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, Entity** entity, float* pickpos, std::vector<Entity*> filterObjectes);
        bool PickAllBodyFilterExclude(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, Entity** entity, float* pickpos, std::vector<Entity*> filterObjectes);
		bool MovePickedBody(const btVector3 &rayFromWorld, const btVector3 &rayToWorld);
		void RemovePickingConstraint();

        Entity* PickEntity(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, float * pickpos);

		void SetDebugPhysicsState(bool s) { m_DebugPhysics = s; }
		bool &GetDebugPhysicsState() { return m_DebugPhysics; }

		void SetClearColor(glm::vec4 color) { m_ClearColor = color; }
		glm::vec4 &GetClearColor() { return m_ClearColor; }

		Texture2D *GetDepthAttachmentTexture2D();
		Texture2D *GetColor0AttachmentTexture2D();

        Texture2D *GetTransparentDepthAttachmentTexture2D();
        Texture2D *GetTransparentColor0AttachmentTexture2D();

        Texture2D *GetCustomDepthAttachmentTexture2D();
        Texture2D *GetCustomColor0AttachmentTexture2D();

        Texture2D *GetCustomDepthAttachmentTexture2DFrom2DScene();
        Texture2D *GetCustomColor0AttachmentTexture2DFrom2DScene();

		uint32_t GetWidth() { return m_ViewportWidth; }
		uint32_t GetHeight() { return m_ViewportHeight; }

		Font *LoadFont(const char *file, std::string name);
		Font *GetFont() { return m_CurrentFont; }

        Entity * AddOrthoCameraNode(Entity *parent = nullptr);
        Entity * AddPerspectCameraNode(Entity *parent = nullptr);
        Entity * AddPerspectCameraNodeForCamera3d(Entity *parent = nullptr);
        Entity * AddLight(Entity *parent = nullptr);
        Entity * AddImageNode(Entity *parent = nullptr, std::string path="", std::string name = "");
        Entity * AddImageNode(Entity *parent = nullptr, Texture2D* texture=nullptr, std::string name = "");
        Entity * AddTextNode(Entity *parent = nullptr, std::string name = "");
        Entity * AddTextFpsNode(Entity *parent = nullptr, std::string name = "");
        Entity * AddButtonNode(Entity *parent = nullptr, std::string name = "");
        Entity * AddEmptyNode(Entity *parent = nullptr, std::string name = "");
        Entity * AddBoxNode(Entity *parent = nullptr, std::string name = "");
        Entity* AddModelNode(Entity *parent = nullptr, std::string path="", std::string name="plane", TextureType textureType = TextureType_ClampToEdge);
        Entity* AddModelNodeSync(Entity *parent = nullptr, std::string path="", std::string name="plane", TextureType textureType = TextureType_ClampToEdge);

        Entity * AddLine(Entity *parent = nullptr, std::string name = "");
        Entity * AddSkybox(std::vector<std::string>& files, Entity *parent = nullptr, std::string name="");

		void AddScriptComponent(Entity *parent = nullptr);
		void AddRigidBodyComponent(Entity *parent = nullptr, RigidBodyShapeType shapeType = RigidBodyShapeType_Box, glm::vec3 trans = glm::vec3(0.0f), glm::vec3 rotation= glm::vec3(0.0f), 
            glm::vec3 scale = glm::vec3(1.0f), glm::vec3 offset=glm::vec3(0.0), float mass = 0.0f, Entity* shapeOjbect = nullptr);
		void AddAnimationComponent(Entity *parent = nullptr);

		void SetLightPos(const glm::vec4 &pos);
		glm::vec3 GetLightPos();

        void SetLightColor(const glm::vec4 &c);
        glm::vec4 GetLightColor();

        void SetLightIntensity(const float& value);
        float GetLightIntensity();

		OrthographicCamera* GetOrthographicCamera() { return m_OrthographicCamera; }
		PerspectiveCamera* GetPerspectiveCamera() { return m_PerspectiveCamera; }
		PerspectiveCamera* GetEditPerspectiveCamera() { return m_EditPerspectiveCamera; }

        void OnEvent(Event &e);

		void SaveToXmlEngine(std::string filepath);
		void ReadFromXmlEngine(std::string filepath);

        void SaveToXml(std::string filepath);
        void ReadFromXml(std::string filepath);

        void SetSanbox(bool b = true) {
            m_IsSanbox = b;
        }

		bool &GetSanbox() { return m_IsSanbox; }
		bool &GetMoveAxisDisplay() { return m_MoveAxisDisplay; }
       
        void SetUseEditCamera(bool r);
        void SaveEditCamera();

		Entity *FindEntityByNameRecursion(const std::string name, Entity *parent = nullptr);
		Entity *FindEntityByRigidBoyRecursion(const btRigidBody *body, Entity *parent = nullptr);

        void SetCurrentSelectEntity(Entity * e);
        Entity *GetCurrentSelectEntity() { return m_CurrentSelectEntity; }

        void SetEntityRigidBodyTransform(Entity *e, glm::vec3 trans);
        void SetEntityRigidBodyRotation(Entity *e, glm::vec3 rotation);
        glm::vec3 GetEntityRigidBodyTransform(Entity *e);
        glm::vec3 GetEntityRigidBodyTransformNoOffset(Entity *e);
        glm::vec3 GetEntityRigidBodyRotation(Entity *e);

		void AddAnimationProperty(AnimationProperty** anim);
		void AddAnimationPropertyGroup(AnimationPropertyGroup* animGroup);

        void AddDesktopIcon(DesktopIconInfo& info);
        void AddPhotoIcon(PhotoIconInfo& info);
        void AddVideoInfo(VideoInfo& info);

        std::vector<DesktopIconInfo>& GetDesktopIcons() { return m_DesktopIconInfos; }
        std::vector<PhotoIconInfo>& GetPhotoIcons() { return m_PhotoIconInfos; }
        std::vector<VideoInfo>& GetVideoInfos() { return m_VideoInfos; }

        void SetFrameBuffer(Framebuffer * f);
        Framebuffer* GetFrameBuffer() { return m_FramebufferData; }

        void SetFrameBuffer2D(Framebuffer * f);
        Framebuffer* GetFrameBuffer2D() { return m_FramebufferData2D; }

        static Renderer2D::Statistics GetStats2D();
        static Renderer3D::Statistics GetStats3D();

        Entity* GetRoot() { return m_root; }
		Entity* m_root;

	private:
		void InitPhysicesWorld();
		void ReleasePhysicesWorld();
		void InitFontUnicode();
		void OnUpdateScriptRecursion(Entity *parent, Timestep ts);
        void onContactTestRecursionOne(Entity *parent);
        void onContactTestRecursion(Entity *current, Entity *other);
		void OnEventScriptRecursion(Entity *parent, Event &e);
		void OnEventMouseAreaRecursion(Entity *parent, Event &e);

		void OnMouseButtonPressedForButton(Event* event, Entity* e);
		void OnMouseButtonReleasedForButton(Event* event, Entity* e);

		void SaveEntityRecursivelyEngine(Entity *parent, tinyxml2::XMLDocument &doc, tinyxml2::XMLElement &xml);
		void SaveEntityRecursively(Entity *parent, tinyxml2::XMLDocument &doc, tinyxml2::XMLElement &xml);
		void AddEntityRecursivelyEngine(Entity *parent, tinyxml2::XMLElement &xml);
		void AddEntityRecursively(Entity *parent, tinyxml2::XMLElement &xml);
        void OnWindowResize(WindowResizeEvent &e);
        void OnWindowFramebufferResize(WindowFramebufferResizeEvent &e);
        void OnMouseButtonMoved(MouseMovedEvent &e);
        
        Entity* CreateArrowAxis(TagType subType, glm::vec4 color, glm::vec3 rotation = glm::vec3(0.0), glm::vec3 shape = glm::vec3(0.0), glm::vec3 shapeOffset = glm::vec3(0.0));
        void SetEntityTrans(Entity *e, glm::vec3 trans);

        void RemoveAnimationProperty(AnimationProperty* anim);

	private:
		uint32_t m_ViewportWidth = 1, m_ViewportHeight = 1;
		bool m_IsRunning = false;
		bool m_IsPaused = false;
		int m_StepFrames = 0;

		std::unordered_map<UUID, Entity *> m_EntityMap;

		btDefaultCollisionConfiguration *collisionConfiguration;
		btCollisionDispatcher *dispatcher;
		btBroadphaseInterface *overlappingPairCache;
		btSequentialImpulseConstraintSolver *solver;
		btDiscreteDynamicsWorld *m_dynamicsWorld;

		btAlignedObjectArray<btCollisionShape *> collisionShapes;

		PerspectiveCamera *m_mainCamera3d = nullptr;

		// data for picking objects
		class btRigidBody *m_pickedBody = nullptr;
		class btRigidBody *m_pickedStaticBody = nullptr;
		class btTypedConstraint *m_pickedConstraint = nullptr;
		int m_savedState;
		btVector3 m_oldPickingPos;
		btVector3 m_hitPos;
		btScalar m_oldPickingDist;

		bool m_DebugPhysics = false;
		bool m_UseEditCamera = false;

		glm::vec4 m_ClearColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);

		Font *m_CurrentFont;

		OrthographicCamera* m_OrthographicCamera;
		OrthographicCamera* m_OrthographicCamera3d;
		PerspectiveCamera* m_PerspectiveCamera;
		PerspectiveCamera* m_EditPerspectiveCamera;

        bool m_IsSanbox = false;//play mode
        bool m_MoveAxisDisplay = false;

        std::string m_scriptData;
        std::string m_scriptDataFunction;

        Entity *m_ArrowGlobalAxisX = nullptr;
        Entity *m_ArrowGlobalAxisY = nullptr;
        Entity *m_ArrowGlobalAxisZ = nullptr;

        Entity *m_CurrentSelectEntity = nullptr;

        DisplayModesEnum m_DisplayMode = DisplayModesEnum_NONE;

		std::vector<AnimationProperty**> m_AnimationPropertyes;
		std::vector<AnimationPropertyGroup*> m_AnimationPropertyeGroups;
        std::vector<DesktopIconInfo> m_DesktopIconInfos;
        std::vector<PhotoIconInfo> m_PhotoIconInfos;
        std::vector<VideoInfo> m_VideoInfos;

        Framebuffer *m_FramebufferData = nullptr;
        Framebuffer *m_FramebufferData2D = nullptr;

        Entity *m_mainCamera2dEntity = nullptr;
        Entity *m_mainCamera3dEntity = nullptr;

        static float m_MouseX;
        static float m_MouseY;

        Entity *m_entityHasFocusMouse = nullptr;
	};    

}

#endif